{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the Winner Take All units\n",
    "class WTU(object):\n",
    "    \n",
    "       \n",
    "    def __init__(self, m, n, dim, num_iterations, eta = 0.5, sigma = None):\n",
    "        \n",
    "        self._m = m\n",
    "        self._n = n\n",
    "        self._neighbourhood = []\n",
    "        self._topography = []\n",
    "        self._num_iterations = int(num_iterations) \n",
    "        self._learned = False\n",
    "        self.dim = dim\n",
    "        \n",
    "        self.eta = float(eta)\n",
    "           \n",
    "        if sigma is None:\n",
    "            sigma = max(m,n)/2.0    # Constant radius\n",
    "        else:\n",
    "            sigma = float(sigma)\n",
    "        self.sigma = sigma\n",
    "        \n",
    "            \n",
    "        print('Network created with dimensions',m,n)\n",
    "            \n",
    "        # Weight Matrix and the topography of neurons\n",
    "        self._W = tf.random.normal([m*n, dim], seed = 0)\n",
    "        self._topography = np.array(list(self._neuron_location(m, n)))\n",
    "            \n",
    "               \n",
    "        \n",
    "    def training(self,x, i):\n",
    "            m = self._m\n",
    "            n= self._n \n",
    "            \n",
    "            # Finding the Winner and its location\n",
    "            d = tf.sqrt(tf.reduce_sum(tf.pow(self._W - tf.stack([x for i in range(m*n)]),2),1))\n",
    "            self.WTU_idx = tf.argmin(d,0)\n",
    "            \n",
    "            slice_start = tf.pad(tf.reshape(self.WTU_idx, [1]),np.array([[0,1]]))\n",
    "            self.WTU_loc = tf.reshape(tf.slice(self._topography, slice_start,[1,2]), [2])\n",
    "            \n",
    "            \n",
    "            # Change learning rate and radius as a function of iterations\n",
    "            learning_rate = 1 - i/self._num_iterations\n",
    "            _eta_new = self.eta * learning_rate\n",
    "            _sigma_new = self.sigma * learning_rate\n",
    "            \n",
    "            \n",
    "            # Calculating Neighbourhood function\n",
    "            distance_square = tf.reduce_sum(tf.pow(tf.subtract(\n",
    "                self._topography, tf.stack([self.WTU_loc for i in range(m * n)])), 2), 1)\n",
    "            neighbourhood_func = tf.exp(tf.negative(tf.math.divide(tf.cast(\n",
    "                distance_square, \"float32\"), tf.pow(_sigma_new, 2))))\n",
    "            \n",
    "            # multiply learning rate with neighbourhood func\n",
    "            eta_into_Gamma = tf.multiply(_eta_new, neighbourhood_func)\n",
    "            \n",
    "            # Shape it so that it can be multiplied to calculate dW\n",
    "            weight_multiplier = tf.stack([tf.tile(tf.slice(\n",
    "                eta_into_Gamma, np.array([i]), np.array([1])), [self.dim])\n",
    "                for i in range(m * n)])\n",
    "            delta_W = tf.multiply(weight_multiplier,\n",
    "                tf.subtract(tf.stack([x for i in range(m * n)]),self._W))\n",
    "            new_W = self._W + delta_W\n",
    "            self._W = new_W\n",
    "            \n",
    "           \n",
    "    def fit(self, X):\n",
    "        for i in range(self._num_iterations):\n",
    "            for x in X:\n",
    "                 self.training(x,i)\n",
    "            \n",
    "        \n",
    "        \n",
    "        # Store a centroid grid for easy retrieval\n",
    "        centroid_grid = [[] for i in range(self._m)]\n",
    "        self._Wts = list(self._W)\n",
    "        self._locations = list(self._topography)\n",
    "        for i, loc in enumerate(self._locations):\n",
    "            centroid_grid[loc[0]].append(self._Wts[i])\n",
    "        self._centroid_grid = centroid_grid\n",
    "\n",
    "        self._learned = True\n",
    "    \n",
    "    def winner(self, x):\n",
    "        idx = self.WTU_idx,self.WTU_loc\n",
    "        return idx\n",
    "             \n",
    "    def _neuron_location(self,m,n):\n",
    "        \n",
    "        for i in range(m):\n",
    "            for j in range(n):\n",
    "                yield np.array([i,j])\n",
    "                \n",
    "                \n",
    "    def get_centroids(self):\n",
    "       \n",
    "        if not self._learned:\n",
    "            raise ValueError(\"SOM not trained yet\")\n",
    "        return self._centroid_grid\n",
    "\n",
    "    def map_vects(self, X):\n",
    "       \n",
    "\n",
    "        if not self._learned:\n",
    "            raise ValueError(\"SOM not trained yet\")\n",
    "\n",
    "        to_return = []\n",
    "        for vect in X:\n",
    "            min_index = min([i for i in range(len(self._Wts))],\n",
    "                            key=lambda x: np.linalg.norm(vect -\n",
    "                                                         self._Wts[x]))\n",
    "            to_return.append(self._locations[min_index])\n",
    "\n",
    "        return to_return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "def normalize(df):\n",
    "    result = df.copy()\n",
    "    for feature_name in df.columns:\n",
    "        max_value = df[feature_name].max()\n",
    "        min_value = df[feature_name].min()\n",
    "        result[feature_name] = (df[feature_name] - min_value) / (max_value - min_value)\n",
    "    return result.astype(np.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Reading input data from file\n",
    "import pandas as pd\n",
    "\n",
    "df = pd.read_csv('colors.csv')  # The last column of data file is a label\n",
    "data = normalize(df[['R', 'G', 'B']]).values\n",
    "name = df['Color-Name'].values\n",
    "n_dim = len(df.columns) - 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Data for Training\n",
    "colors = data\n",
    "color_names = name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Network created with dimensions 30 30\n"
     ]
    }
   ],
   "source": [
    "som = WTU(30, 30, n_dim, 400, sigma=10.0)\n",
    "som.fit(colors)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Get output grid\n",
    "image_grid = som.get_centroids()\n",
    "\n",
    "# Map colours to their closest neurons\n",
    "mapped = som.map_vects(colors)\n",
    "\n",
    "# Plot\n",
    "plt.imshow(image_grid)\n",
    "plt.title('Color Grid SOM')\n",
    "for i, m in enumerate(mapped):\n",
    "    plt.text(m[1], m[0], color_names[i], ha='center', va='center',\n",
    "             bbox=dict(facecolor='white', alpha=0.5, lw=0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(603, shape=(), dtype=int64) tf.Tensor([20  3], shape=(2,), dtype=int64)\n"
     ]
    }
   ],
   "source": [
    "idx, loc = som.winner([0.5, 0.5, 0.5])\n",
    "print(idx, loc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:new_tf]",
   "language": "python",
   "name": "conda-env-new_tf-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
